package org.acghub.common;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.function.Supplier;

public class ObjectUtils
{

    public static boolean isNull(Object obj)
    {
        return obj == null;
    }

    public static boolean isNotNull(Object obj)
    {
        return obj != null;
    }

    public static boolean isEmpty(Object obj)
    {
        if (isNull(obj))
        {
            return true;
        }
        else if (obj instanceof CharSequence)
        {
            return ((CharSequence) obj).length() == 0;
        }
        else if (obj instanceof StringJoiner)
        {
            return ((StringJoiner) obj).length() == 0;
        }
        else if (obj instanceof Collection)
        {
            return ((Collection<?>) obj).isEmpty();
        }
        else if (obj instanceof Map)
        {
            return ((Map<?, ?>) obj).isEmpty();
        }
        else if (obj instanceof Iterable)
        {
            Iterator<?> iterator = ((Iterable<?>) obj).iterator();
            return !iterator.hasNext();
        }
        else if (obj.getClass().isArray())
        {
            return Array.getLength(obj) == 0;
        }
        return false;
    }

    public static boolean isNotEmpty(Object obj)
    {
        return !isEmpty(obj);
    }

    public static int size(Object o)
    {
        if (isNull(o))
        {
            return 0;
        }
        else if (o instanceof CharSequence)
        {
            return ((CharSequence) o).length();
        }
        else if (o instanceof StringJoiner)
        {
            return ((StringJoiner) o).length();
        }
        else if (o instanceof Collection)
        {
            return ((Collection<?>) o).size();
        }
        else if (o instanceof Map)
        {
            return ((Map<?, ?>) o).size();
        }
        else if (o instanceof Iterable)
        {
            int size = 0;
            for (Object __ : (Iterable<?>) o)
            {
                size++;
            }
            return size;
        }
        else if (o instanceof Object[])
        {
            return ((Object[]) o).length;
        }
        else if (o.getClass().isArray())
        {
            return Array.getLength(o);
        }
        return 1;
    }

    public static <K, V> Pair<K, V> pair(Map<K, V> map)
    {
        if (isEmpty(map))
        {
            return null;
        }
        for (Map.Entry<K, V> entry : map.entrySet())
        {
            return new Pair<>(entry.getKey(), entry.getValue());
        }
        return null;
    }

    public static <K, V> List<Pair<K, V>> pairs(Map<K, V> map)
    {
        if (isEmpty(map))
        {
            return Collections.emptyList();
        }
        List<Pair<K, V>> pairs = new ArrayList<>(map.size());
        for (Map.Entry<K, V> entry : map.entrySet())
        {
            pairs.add(new Pair<>(entry.getKey(), entry.getValue()));
        }
        return pairs;
    }

    public static void assertNonNull(Object obj, String message)
    {
        if (isNull(obj))
        {
            throw new NullPointerException(message);
        }
    }

    public static void assertTrue(boolean expression, String message)
    {
        if (!expression)
        {
            throw new RuntimeException(message);
        }
    }

    public static <E extends Throwable> void assertTrue(boolean expression, Supplier<E> es) throws E
    {
        if (!expression)
        {
            throw es.get();
        }
    }

    public static <T> T defaultIfNull(T value, T defaultValue)
    {
        return isNull(value) ? defaultValue : value;
    }

    public static <T> T defaultIfEmpty(T value, T defaultValue)
    {
        return isEmpty(value) ? defaultValue : value;
    }

}
